#include <OgreRoot.h>
#include <OgreFrameListener.h>
#include <OgreWindowEventUtilities.h>
#include <OgreConfigFile.h>
#include <OgreRenderWindow.h>
#include <OgreSceneManager.h>
#include <CEGUI/CEGUI.h>
#include <OgreCEGUIRenderer.h>

#include "application.h"
#include "SettingsManager.h"
#include "Camera.h"

using namespace Ogre;
extern Application app;

Application* gApplication;
//global variables
/**********************************************************************/
SettingsManager gSettings;
SceneManager* gSceneMgr;
OMGCamera* gCamera;

// Physics SDK globals
NxPhysicsSDK*	gPhysicsSDK = NULL;
NxScene*		gScene = NULL;
NxVec3			gDefaultGravity(0,-9.8,0);

// Actor globals
NxActor* groundPlane = NULL;

// Focus actor
NxActor* gSelectedActor = NULL;
/**********************************************************************/


CEGUI::MouseButton convertButton(OIS::MouseButtonID buttonID)
{
    switch (buttonID)
    {
    case OIS::MB_Left:
        return CEGUI::LeftButton;

    case OIS::MB_Right:
        return CEGUI::RightButton;

    case OIS::MB_Middle:
        return CEGUI::MiddleButton;

    default:
        return CEGUI::LeftButton;
    }
}


class EventListener : 
	public FrameListener , 
	public WindowEventListener , 
	public OIS::MouseListener ,
	public OIS::KeyListener
{
public:
	EventListener(OIS::Keyboard *keyboard, OIS::Mouse *mouse)
     : mKeyboard(keyboard), mMouse(mouse)
    {
		mQuitRequested=false;
		mMouse->setEventCallback(this);
		mKeyboard->setEventCallback(this);
		
    }

    // MouseListener
    bool mouseMoved(const OIS::MouseEvent &arg)
    {	
		CEGUI::System::getSingleton().injectMouseMove(arg.state.X.rel, arg.state.Y.rel);
        return true;
    }

    bool mousePressed(const OIS::MouseEvent &arg, OIS::MouseButtonID id)
    {
		CEGUI::System::getSingleton().injectMouseButtonDown(convertButton(id));
        return true;
    }

    bool mouseReleased(const OIS::MouseEvent &arg, OIS::MouseButtonID id)
    {
		CEGUI::System::getSingleton().injectMouseButtonUp(convertButton(id));
        return true;
    }

    // KeyListener
    bool keyPressed(const OIS::KeyEvent &arg)
    {
		CEGUI::System *sys = CEGUI::System::getSingletonPtr();
		sys->injectKeyDown(arg.key);
		sys->injectChar(arg.text);

		if (arg.key == OIS::KC_F1) gSettings.mDebugGeometry=!gSettings.mDebugGeometry;
		return true;
    }

    bool keyReleased(const OIS::KeyEvent &arg)
    {
		CEGUI::System::getSingleton().injectKeyUp(arg.key);
        return true;
    }

    bool frameStarted(const FrameEvent& evt)
    {
        app.mSession->update(evt.timeSinceLastFrame);
        mKeyboard->capture();
		mMouse->capture();
        return !mQuitRequested;
    }

	void windowClosed (RenderWindow *rw)
	{
		mQuitRequested=true;
	}

	bool mQuitRequested;
private:
    OIS::Keyboard *mKeyboard;
	OIS::Mouse *mMouse;
};




void Application::go()
{
	gApplication=this;

    gSettings.parseSection("Bootstrap"); // get the boot specific variables
    createRoot();
    defineResources();
    setupRenderSystem();
    createRenderWindow();
    initializeResourceGroups();
    setupScene();
    setupInputSystem();
    setupCEGUI();
    createFrameListener();
    startRenderLoop();
	
}

Application::~Application()
{
    mInputManager->destroyInputObject(mKeyboard);
	mInputManager->destroyInputObject(mMouse);
    OIS::InputManager::destroyInputSystem(mInputManager);

    delete mSystem;
    delete mRenderer;

	delete mSession;

    delete mListener;
    delete mRoot;

}

void Application::createRoot()
{
    #if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32
		#ifdef _DEBUG
			mRoot = new Root("cfg/plugins_win32d.cfg");
		#else
			mRoot = new Root("cfg/plugins_win32.cfg");
        #endif
    #else
	    mRoot = new Root("cfg/plugins_unix.cfg");
    #endif
}

void Application::defineResources()
{
    String secName, typeName, archName;
    ConfigFile cf;
    cf.load("cfg/resources.cfg");
    ConfigFile::SectionIterator seci = cf.getSectionIterator();
    while (seci.hasMoreElements())
    {
        secName = seci.peekNextKey();
        ConfigFile::SettingsMultiMap *settings = seci.getNext();
        ConfigFile::SettingsMultiMap::iterator i;
        for (i = settings->begin(); i != settings->end(); ++i)
        {
            typeName = i->first;
            archName = i->second;
            ResourceGroupManager::getSingleton().addResourceLocation(archName, typeName, secName);
        }
    }

}

void Application::setupRenderSystem()
{
    if (!mRoot->restoreConfig() && !mRoot->showConfigDialog())
        throw Exception(52, "User canceled the config dialog!", "Application::setupRenderSystem()");

/*
    // Do not add this to the application
    RenderSystem *rs = mRoot->getRenderSystemByName("Direct3D9 Rendering Subsystem");
                                            // or use "OpenGL Rendering Subsystem"
    mRoot->setRenderSystem(rs);
    rs->setConfigOption("Full Screen", "No");
    rs->setConfigOption("Video Mode", "800 x 600 @ 32-bit colour");*/
}

void Application::createRenderWindow()
{
    mRenderWindow=mRoot->initialise(true, "omgwheel");
	
}

void Application::initializeResourceGroups()
{

    ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
}

void Application::setupScene()
{
    gSceneMgr = mRoot->createSceneManager(ST_GENERIC, "Default SceneManager");
    gCamera=new OMGCamera();
    //Viewport *vp = mRoot->getAutoCreatedWindow()->addViewport(cam);
    mRoot->getAutoCreatedWindow()->addViewport(gCamera->mOgreCamera);

    mSession=new Session();
	mSession->loadObjects("data/maps/objects.txt");
}

void Application::setupInputSystem()
{
    size_t windowHnd = 0;
    std::ostringstream windowHndStr;
    OIS::ParamList pl;
    RenderWindow *win = mRoot->getAutoCreatedWindow();

    win->getCustomAttribute("WINDOW", &windowHnd);
    windowHndStr << windowHnd;
    pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));
    mInputManager = OIS::InputManager::createInputSystem(pl);
    try
    {
        mKeyboard = static_cast<OIS::Keyboard*>(mInputManager->createInputObject(OIS::OISKeyboard, true));
        mMouse = static_cast<OIS::Mouse*>(mInputManager->createInputObject(OIS::OISMouse, true));
    }
    catch (const OIS::Exception &e)
    {
        throw Exception(42, e.eText, "Application::setupInputSystem");
    }

}

void Application::setupCEGUI()
{
    SceneManager *mgr = mRoot->getSceneManager("Default SceneManager");
    RenderWindow *win = mRoot->getAutoCreatedWindow();

    // CEGUI setup
    mRenderer = new CEGUI::OgreCEGUIRenderer(win, Ogre::RENDER_QUEUE_OVERLAY, false, 3000, mgr);
    mSystem = new CEGUI::System(mRenderer);

	// activate gui scheme -- specs for skin
	CEGUI::SchemeManager::getSingleton().loadScheme((CEGUI::utf8*)"omg.scheme");

	// init a layout


	if (gSettings.mShowMenu)
	{
		mSystem->setDefaultMouseCursor((CEGUI::utf8*)"OMG-Images", (CEGUI::utf8*)"ScrollbarUpNormal");
		CEGUI::MouseCursor::getSingleton().setImage(CEGUI::System::getSingleton().getDefaultMouseCursor());

	//mSheet = CEGUI::WindowManager::getSingleton().loadWindowLayout((CEGUI::utf8*)"in-game.layout"); 
	//mSystem->setGUISheet(mSheet);
	


		mMenu=new Menu();
		mCreditsMenu=new CreditsMenu();
		mHighscoresMenu=new HighscoresMenu();
		mPlay_GameMenu=new Play_GameMenu();
		mExitMenu=new ExitMenu();
		
		mMenu->show();
	}

}

void Application::createFrameListener()
{
    mListener = new EventListener(mKeyboard, mMouse);
    mRoot->addFrameListener(mListener);
	
	
	WindowEventUtilities::addWindowEventListener(mRenderWindow,mListener);
}

void Application::startRenderLoop()
{
    mRoot->startRendering();
}
